﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UdpTransceiver;
using ParametersParser;
using System.Net;
using System.Threading;

namespace Exchange
{
    /// <summary>
    /// Класс - коммуникатор. Обеспечивает обмен приложения с приложениями на удаленных узлах
    /// </summary>
    public class Communicator
    {
        /// <summary>
        /// Параметры соединения коммуникатора
        /// </summary>
        private static IPAddress m_localIP;
        private static int m_localPort;
        private static IPAddress m_remoteIP;
        private static int m_remotePort;
        private static UDPReceiver m_receiver;
        public CommunicatorState state;

        /// <summary>
        /// Поток для работы обменника коммуниктора
        /// </summary>
        Thread NetworkThread;
        /// <summary>
        /// Список параметров, которыми обменивается коммуникатор
        /// </summary>
        public List<Parameter> Parameters;
        /// <summary>
        /// Конструктор коммуникатора
        /// </summary>
        public Communicator(string localIP, int localPort, string remoteIP, int remotePort)
        {
            m_localIP = IPAddress.Parse(localIP);
            m_localPort = localPort;
            m_remoteIP = IPAddress.Parse(remoteIP);
            m_remotePort = remotePort;
            // Создаем приемник
            m_receiver = new UDPReceiver(m_localIP, m_localPort, m_remoteIP, m_remotePort);
            // Создаем пустой список параметров.
            Parameters = new List<Parameter>();
            // Создаем поток для работы обменника
            NetworkThread = new Thread(ListenNetwork);
        }
        /// <summary>
        /// Запускает в работу обменник
        /// </summary>
        public void Start()
        {
            state = CommunicatorState.Active;
            NetworkThread.Start();
        }
        /// <summary>
        /// Останавливает работу обменника
        /// </summary>
        public void Stop()
        {
            state = CommunicatorState.Stoped;
            NetworkThread.Abort();
        }
        /// <summary>
        /// Добавление параметра в список для обмена
        /// </summary>
        /// <param name="Name">Имя нового параметра</param>
        /// <param name="Direction">Вид параметра по направлению передачи</param>
        public void AddParameter(string id, string Name, ParameterDirection Direction, double priority)
        {
            Parameters.Add(new Parameter(id,Name, Direction,"",priority,0,"Test"));
        }
        /// <summary>
        /// Удаление параметра из списка обменника по имени
        /// </summary>
        /// <param name="Name"></param>
        public void DeleteParameter(string Name)
        {

        }
        /// <summary>
        /// Проверка списка сообщения на наличие клонов
        /// </summary>
        public bool CheckParametersID(Parameter item)
        {
            foreach (Parameter obj in Parameters)
            {
                if (item.Id == obj.Id && item.Name != obj.Name)
                {
                    if (item.LastUpdateTime > obj.LastUpdateTime)
                    {
                        Parameters.Remove(obj);
                    }
                    else
                    {
                        Parameters.Remove(item);
                    }
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Определение сообщения с наивысшим приоритетом
        /// </summary>
        /// <param name="toPlay">Сообщение для проигрывания</param>
        public Parameter ChooseToPlay(Parameter toPlay)
        {
            int i = -1;
            int j = 0;
            foreach (Parameter obj in Parameters)
            {
                i++;
                if (obj.Priority < toPlay.Priority)
                {
                    j = i;
                    toPlay = obj;
                }
                else if (obj.Priority == toPlay.Priority)
                     {
                         if (obj.LastUpdateTime > toPlay.LastUpdateTime)
                         {
                             j = i;
                             toPlay = obj;
                         }
                     }
            }
            return toPlay;
        }

        /// <summary>
        /// Осуществляет обмен коммуникатора с удаленным узлом
        /// </summary>
        private void ListenNetwork()
        {
            while (state == CommunicatorState.Active)
            {
                //Получаем датаграмму
                UDPDatagram currentDatagramm = m_receiver.ReceiveDatagram();
                //Преобразуем датаграмму в сообщение
                var currentMessage = new ModelMessage(currentDatagramm.MessageBody);

                //Console.WriteLine("{0} : Package of type {1} received.", DateTime.Now, currentDatagramm.PackageType);
                //Если пришел информационный пакет
                if (currentDatagramm.PackageType == PackageTypes.Info)
                {
                    // Перебираем все параметры из списка обменика
                    foreach (Parameter Item in Parameters)
                    {
                        // Если параметр входящий или двунаправленый
                        if (Item.Direction == ParameterDirection.Input || Item.Direction == ParameterDirection.Inpout)
                        {
                            // Пробуем выделить параметр с таким именем из входящего пакета
                            var paramValue = currentMessage.GetParameter(Item.Name);
                            // Если такой параметр есть в пакете обновляем атрибуты параметра в обменнике
                            if (paramValue != null)
                            {
                                Item.Value = paramValue.ToString();
                                Item.LastUpdateTime = DateTime.Now;
                                Item.UpdateCounter++;
                                Item.LastIPReceiving = m_remoteIP;
                                Item.LastPortReceiving = m_remotePort;
                                Item.NeedPlay = true;
                            }
                        }
                      
                    }
                }
                // Если принятый пакет - это признак готовности главной машины к приему
                else if (currentDatagramm.PackageType == PackageTypes.Ready)
                {
                    foreach (Parameter Item in Parameters)
                    {
                        if (Item.Direction == ParameterDirection.Output || Item.Direction == ParameterDirection.Inpout)
                        {
                            var paramValue = currentMessage.GetParameter(Item.Name);
                            //получаем значение параметра из сообщения
                            if (paramValue != null)
                            {
                                lock (Item.Value)
                                {
                                    Item.Value = paramValue.ToString();
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
